#include "ProcessPool.hpp"

int main()
{
    // 这个代码，有一个藏得比较深的bug --- TODO
    // 创建进程池对象
    ProcessPool pp(gdefaultnum);

    //启动进程池
    pp.Start();

    //自动派发任务
    int cnt = 2;
    while(cnt--)
    {
        pp.Run();
        sleep(1);
    }

    //回收，结束进程
    pp.Stop();

    return 0;
}
//从代码逻辑来看，Start 函数和 Run 函数是独立调用的，Run 并不会 “调用到 Start 函数里面”。你看到的现象可能是父子进程并行执行导致的 “错觉”，我们拆解一下完整运行流程：
//一、整体运行逻辑（分步骤）
//创建进程池对象ProcessPool pp(gdefaultnum);
//初始化 _process_num（默认 5），TaskManager 注册 3 个任务函数（PrintLog/Download/Upload），ChannelManager 自动初始化（_channels 为空，_next=0）。
//调用 Start()：创建子进程和管道这是核心初始化步骤，循环创建 5 个子进程，每个子进程对应一个管道：
//父进程：创建管道 pipefd[0]（读端）和 pipefd[1]（写端），fork 子进程后，关闭读端 pipefd[0]，仅保留写端，通过 ChannelManager::Insert 记录写端和子进程 PID（_channels 中存储 5 个 Channel 对象）。
//子进程：关闭写端 pipefd[1]，仅保留读端，进入 Work 函数阻塞等待（read(rfd, ...)），直到收到父进程发送的任务码或管道被关闭。
//至此，Start() 执行完毕，父进程拥有 5 个管道写端，5 个子进程分别阻塞在 read 等待任务。
//调用 Run() 10 次：分发任务每次 Run() 做两件事：
//生成随机任务码（0/1/2，对应 3 个任务）。
//通过 ChannelManager::Select 轮询选择一个 Channel（即一个子进程的管道写端），调用 Send 函数通过 write 向管道写入任务码。
//子进程此时正在 Work 函数中阻塞 read，收到任务码后立即执行对应任务（如 PrintLog），然后继续阻塞等待下一个任务。
//调用 Stop()：结束子进程并回收
//父进程关闭所有管道写端（Channel::Close），子进程的 read 会返回 0，触发 break 退出 Work 循环，子进程终止。
//父进程通过 waitpid 回收所有子进程资源，避免僵尸进程。
//二、为什么会觉得 Run 调用了 Start？
//可能的原因是父子进程并行执行的输出交织：
//Start 中 fork 出的子进程会独立运行，它们的输出（如 “子进程收到任务码”）可能在 Run 执行时才打印出来，看起来像是 Run 触发了 Start 中的逻辑。
//实际流程是：Start 先完成所有子进程和管道的创建，之后 Run 仅负责通过管道发送任务，子进程则在后台持续等待并处理任务，两者是 “生产者 - 消费者” 的并行关系。
//简单说：Start 是 “搭台子”（创建子进程和通信管道），Run 是 “送任务”（通过管道发任务码），Stop 是 “拆台子”（关闭管道、回收子进程），三者依次调用，Run 不会重复执行 Start 的逻辑。